/*
 * Project: CareerAdmin
 * @(#)$Id: ApplicationFilter.java,v 1.1 2006/09/21 22:05:17 jjohnston Exp $
 * 
 * This Software is confidential and copyrighted.
 * Copyright  2003, 2005 Room & Board, Inc.  All Rights Reserved.
 * 
 * This software is the proprietary information of Room & Board, Inc.  Use 
 * is subject to license terms.
 * 
 * Title to Software and all associated intellectual property rights is retained 
 * by Room & Board, Inc. and/or its licensors.
 * 
 * Except as specifically authorized in any Supplemental License Terms, this 
 * software may not be copied.  Unless enforcement is prohibited by 
 * applicable law, you may not modify, decompile, reverse engineer this 
 * Software without the express written permission of Room & Board, Inc.
 * 
 * No right, title or interest in or to any trademark, service mark, logo or 
 * trade name of Room & Board, Inc. or its licensors is granted under 
 * this Agreement.
 */
package com.perselab.recipe.dao.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;

/**
 * Creates a command to wrap the Hibernate criteria API to filter.
 * 
 * @since 2.0
 */
public class PersistentFilter implements CriteriaCommand {
	/**
	 * Filter list
	 */
    List<Filter> filters = new ArrayList<Filter>();
    
    List<Filter> orFilters = new ArrayList<Filter>();
    
    Set<String> alias = new HashSet<String>();
    
    /**
     * Add alias
     * @param alias
     */
    public void addAlias(String _alias){
    	if(!this.alias.contains(_alias)){
    		this.alias.add(_alias);
    	}
    }
    
    /**
     * Add alias
     * @param alias
     */
    @Deprecated
    public void addAlias(List<String> alias){
    	this.alias.addAll(alias);
    }
    /**
     * Adds filter
     * @param property
     * @param value
     * @param operator 1 is 'and' 0 is 'or'
     */
    public void addFilter(String property, Object value, int operator) {
    	if(operator == 1){
    		filters.add(new Filter(property, value));
    	}else{
    		orFilters.add(new Filter(property, value));
    	}
    }

    /**
     * @see CriteriaCommand#execute(Criteria)
     */
    public Criteria execute(Criteria criteria) {
    	for(String value : this.alias){
    		criteria.createAlias(value, value);
    	}
    	
        for (Filter filter : filters) {
            buildCriteria(criteria, filter.getProperty(), filter.getValue());
        }
        
        Criterion expOr = buildOrCriteria(orFilters, 0);
        if(expOr != null){
        	criteria.add(expOr);
        }

        return criteria;
    }
    
    /**
     * toHQL string
     * @return
     */
    public String userFiltersToHQL(){
    	StringBuffer hql = new StringBuffer();
    	return hql.toString();
    }

    /**
     * Build criteria
     * @param criteria
     * @param property
     * @param value
     */
    private void buildCriteria(Criteria criteria, String property, Object value) {
        if (value != null) {              
        	criteria.add(Restrictions.eq(property, value));
        }
    }
    
    /**
     * buildOrCriteria
     * @param filters
     * @param next
     * @return
     */
    private Criterion buildOrCriteria(List<Filter> filters, int next){
    	if(next < 0 || next >= filters.size())
    		return null;
    	
    	Filter filter = filters.get(next);
    	if(next+1 < filters.size()){
        	return Restrictions.or(buildLikeExpression(filter.getProperty(), filter.getValue()), 
        			buildOrCriteria(filters, ++next));
    	}else{
    		return buildLikeExpression(filter.getProperty(), filter.getValue());
    	}
    }    
    
    /**
     * Build like expression
     * @param property
     * @param value
     * @return
     */
    private Criterion buildLikeExpression(String property, Object value){
    	if(value == null)
    		return null;
    	// assume the filter property is String type
    	// workarround for date type
    	if(value instanceof Date){
    		Date date = (Date) value;
    		Calendar startDate = Calendar.getInstance();
    		startDate.setTime(date);
    		startDate.set(Calendar.HOUR, 0);
    		startDate.set(Calendar.MINUTE, 0);
    		startDate.set(Calendar.SECOND, 0);
    		
    		Calendar endDate = Calendar.getInstance();
    		endDate.setTime(date);
    		endDate.set(Calendar.HOUR, 23);
    		endDate.set(Calendar.MINUTE, 59);
    		endDate.set(Calendar.SECOND, 59);
    		
    		return Restrictions.between(property, startDate.getTime(), endDate.getTime());
    	}else{
    		return Restrictions.like(property, value.toString(), MatchMode.ANYWHERE);
    	}        
    }

    /**
     * Filter class, holding property name and value
     * 
     */
    private static class Filter {
    	/** property */
        private final String property;
        /** value */
        private final Object value;

        /**
         * Constructor
         * @param property
         * @param value
         */
        public Filter(String property, Object value) {
            this.property = property;
            this.value = value;      
        }

        /**
         * Gets property
         * @return
         */
        public String getProperty() {
            return property;
        }

        /**
         * Gets value
         * @return
         */
        public Object getValue() {
            return value;
        }
    }
}
